import { 
  Disposable, 
  Webview, 
  WebviewView,
  WebviewPanel,
  ViewColumn,
  WebviewViewResolveContext,
  CancellationToken,
  ExtensionContext,
  window,
  Uri,
  ColorThemeKind
} from "vscode";
import { getUri } from "../utils/getUri";
import { getNonce } from "../utils/getNonce";
import axios, { AxiosRequestConfig } from 'axios';
import * as https from 'https';
import { Readable } from 'stream';
import * as fs from 'fs';
import * as path from 'path';
import { TosClient, TosClientError, TosServerError } from '@volcengine/tos-sdk';
const { CozeAPI,
  COZE_CN_BASE_URL,
  ChatEventType,
  CreateChatData } = require('@coze/api');
import OpenAI from "openai";

// 定义manifest.json的类型接口
interface Manifest {
  router: {
    entry: string;
    pages: Record<string, { component: string }>;
  };
  [key: string]: any; // 其他属性用任意类型表示
}

export class LuHuoPanel {
  public static currentPanel: LuHuoPanel | undefined;
  private readonly _panel: WebviewPanel;
  private _extensionUri: Uri;
  private _disposables: Disposable[] = [];
  private _axiosInstance;
  private _tosClient;
  private _cozeApi;
  private _imgBotId = '7468577345419952154';
  private _openai: OpenAI;
  private _systemPrompt = `
            # 角色
            你是一位专业且经验丰富的HTML和CSS开发者，尤其擅长设计色彩丰富鲜明的界面。你的任务是依据用户的要求，精准生成符合要求的HTML代码。
            
            ## 你必须遵守以下规则
            - 基于用户的要求，生成单页应用的HTML代码。代码需写在<html></html>标签内，必须要有<body><div class="container"></div></body>标签，其他标签代码写在<div class="container"></div>标签内。
            - container的样式宽度为100%，高度为100%并且不能使用padding和margin。
            - 设计尺寸为手机屏幕尺寸，自适应屏幕。
            - 布局只能采用flex布局。
            - 父标签的CSS样式都必须有display: flex;。
            - body只能有背景色，背景色和container的背景色相同，body其他样式都不能有，不能有padding和margin。
            - CSS中不能有任何伪类选择器，包括:hover、:first-child、:last-child等伪类选择器。
            - CSS中只能用background-color，不能用其他的background。
            - CSS中不要使用flex:、gap:、align-self:、transition:、box-shadow:、text-shadow:、backdrop-filter:、border: none;、box-sizing:等样式。
            - CSS中的长度单位只能用px，不能使用百分比、100vw、100vh等其他单位。
            - CSS中padding、margin只能使用px不能用其他单位。
            - CSS中line-height只能使用px。
            - 文本标签和按钮只能用span标签，div标签不能有文字。
            - 不能出现<br>标签。
            - CSS代码需写在style标签内，无需考虑浏览器兼容性。
            - 确保生成的应用在视觉上要精准匹配背景颜色、文字颜色、字体大小、字体家族、内边距、外边距、边框等细节。
            - 对于图片，请使用 https://placehold.co 的占位图片，尽可能带有各种颜色。
            - 如果需要重复元素，例如有 15 个项目，代码中必须包含 15 个项目。不要留类似 <!-- Repeat for each news item --> 这样的注释，否则会导致问题。
            - 仅返回<html></html>标签内的完整代码。
            - 请勿在代码开始或结束处包含markdown "\`\`\`" 和 "\`\`\`html" 标识。
            - 不要在代码中添加类似 <!-- Add other navigation links as needed --> 或 <!-- ... other news items ... --> 的注释来代替完整的代码，请编写完整代码。
            `;
  private _controllers = new Map();
  private _createChatDatas = new Map();

  constructor(panel: WebviewPanel, private readonly _context: ExtensionContext) {
    this._panel = panel;
    this._extensionUri = _context.extensionUri;
    
    this._panel.onDidDispose(() => this.dispose(), null, this._disposables);

    this._panel.webview.html = this._getWebviewContent(this._panel.webview);

    this._setWebviewMessageListener(this._panel.webview);

    // 监听主题变化
    this._context.subscriptions.push(
      window.onDidChangeActiveColorTheme(() => {
        this._postThemeMessage();
      })
    );

    // 创建 axios 实例
    this._axiosInstance = axios.create({
      httpsAgent: new https.Agent({  
        rejectUnauthorized: false // 忽略 SSL 证书验证
      })
    });

    this._tosClient = new TosClient({
      accessKeyId: "AKLTY2I2ZWM2MzlhMjE1NDQyZGJlZjJmYWNjYzBjZTY5NGE", 
      accessKeySecret: "TnpjNVlUZ3daR0V5TjJFNU5EUTNPV0kyTXpVME9ERXpOREZoTmpjNE5tTQ==", 
      region: "cn-guangzhou", // 填写 Bucket 所在地域。以华北2（北京)为例，"Provide your region" 填写为 cn-beijing。
      endpoint: "tos-cn-guangzhou.volces.com", // 填写域名地址
    });

    this._cozeApi = new CozeAPI({
        baseURL: COZE_CN_BASE_URL,
        token: 'pat_v0MfpHuMyhncqSMjurOzFeaBXoIRpKcydeBpnxlhyhbUii7Fw83RRB3Euivp18JR'
    });

    this._openai = new OpenAI({
        apiKey: 'sk-d8e5f95d6f1440da811ae70bce345260',
        baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
    });
  }

  public static render(_context: ExtensionContext) {
    if (LuHuoPanel.currentPanel) {
      // If the webview panel already exists reveal it
      LuHuoPanel.currentPanel._panel.reveal(ViewColumn.One, true);
    } else {
      // If a webview panel does not already exist create and show a new one
      const panel = window.createWebviewPanel(
        // Panel view type
        "showPanelView",
        // Panel title
        "LUHUO",
        // The editor column the panel should be displayed in
        ViewColumn.One,
        // Extra panel configurations
        {
          // Enable JavaScript in the webview
          enableScripts: true,
          retainContextWhenHidden: true,
          // Restrict the webview to only load resources from the `out` and `webview-ui/build` directories
          localResourceRoots: [
            Uri.joinPath(_context.extensionUri, "out"),
            Uri.joinPath(_context.extensionUri, "webview-ui/build"),
          ]
        }
      );

      LuHuoPanel.currentPanel = new LuHuoPanel(panel, _context);
    }
  }


  public dispose() {
    LuHuoPanel.currentPanel = undefined;

    // Hide the current webview panel instead of disposing it
    this._panel.dispose();

    // Dispose of all disposables (i.e. commands) for the current webview panel
    while (this._disposables.length) {
      const disposable = this._disposables.pop();
      if (disposable) {
        disposable.dispose();
      }
    }
  }

  private async _handleHttpRequest(message: any) {
    try {
      const config: AxiosRequestConfig = {
        url: message.url,
        method: message.method || 'GET',
        headers: message.headers || {},
        data: message.data,
        params: message.params,
      };

      const response = await this._axiosInstance(config);

      if (this._panel) {
        this._panel.webview.postMessage({
          command: 'httpResponse',
          requestId: message.requestId,
          data: response.data,
          status: response.status,
          headers: response.headers
        });
      }
    } catch (error: any) {
      if (this._panel) {
        this._panel.webview.postMessage({
          command: 'httpError',
          requestId: message.requestId,
          error: {
            message: error.message,
            response: error.response?.data,
            status: error.response?.status
          }
        });
      }
    }
  }

  private async _handleStreamChat(message: any) {
    const { type, additional_messages } = message
    if (type === 'img') {
      console.log(additional_messages)
      const v = await this._cozeApi.chat.stream({
        bot_id: this._imgBotId,
        auto_save_history: false,
        additional_messages: additional_messages
      })
  
      for await (const part of v) {
        if (part.event === ChatEventType.CONVERSATION_CHAT_CREATED) {
          console.log('[START]', part.data);
          this._createChatDatas.set(message.requestId, part.data);
        } else if (part.event === ChatEventType.CONVERSATION_MESSAGE_DELTA) {
          const { role, type, content } = part.data;
          if (role === 'assistant' && type === 'answer') {
            if (this._panel) {
              this._panel.webview.postMessage({
                command: 'sseData',
                requestId: message.requestId,
                data: content
              });
            }
          }
        } else if (part.event === ChatEventType.CONVERSATION_MESSAGE_COMPLETED) {
          const { role, type, content } = part.data;
          if (role === 'assistant' && type === 'answer') {
            
          } else {
            console.log('[%s]:[%s]:%s', role, type, content);
          }
        } else if (part.event === ChatEventType.CONVERSATION_CHAT_COMPLETED) {
          console.log(part.data.usage);
          if (this._panel) {
            this._panel.webview.postMessage({
              command: 'sseComplete',
              requestId: message.requestId
            });
          }
        } else if (part.event === ChatEventType.DONE) {
          console.log(part.data);
        } else if (part.event === ChatEventType.ERROR) {
          console.error(part.data);
          if (this._panel) {
            this._panel.webview.postMessage({
              command: 'sseError',
              requestId: message.requestId,
              error: part.data
            });
          }
        }
      }
    } else {
      let isAnswering = false;
      const abortController = new AbortController();
      this._controllers.set(message.requestId, abortController);
      additional_messages.unshift({
        role: 'system',
        content: this._systemPrompt
      })
      console.log(additional_messages)
      const completion = await this._openai.chat.completions.create({
          model: message.model,
          messages: additional_messages,
          stream: true,
          // 解除以下注释会在最后一个chunk返回Token使用量
          // stream_options: {
          //     include_usage: true
          // }
      }, {
        signal: abortController.signal
      });

      for await (const chunk of completion) {
        // 处理usage信息
        if (!chunk.choices?.length) {
            console.log("\n" + "=".repeat(20) + "Token 使用情况" + "=".repeat(20) + "\n");
            console.log(chunk.usage);
            continue;
        }

        const delta: any = chunk.choices[0].delta;

        // 处理空内容情况
        if (!delta.reasoning_content && !delta.content) {
            continue;
        }

        // 处理开始回答的情况
        if (!delta.reasoning_content && !isAnswering) {
            console.log("\n" + "=".repeat(20) + "完整回复" + "=".repeat(20) + "\n");
            isAnswering = true;
        }

        // 处理思考过程
        if (delta.reasoning_content) {
            if (this._panel) {
              this._panel.webview.postMessage({
                command: 'sseReasoningData',
                requestId: message.requestId,
                data: delta.reasoning_content
              });
            }
        }
        // 处理回复内容
        if (delta.content) {
            if (this._panel) {
              this._panel.webview.postMessage({
                command: 'sseData',
                requestId: message.requestId,
                data: delta.content
              });
            }
        }
      }

      if (this._panel) {
        this._panel.webview.postMessage({
          command: 'sseComplete',
          requestId: message.requestId
        });
      }

    }
  }

  private async _handleStreamRequest(message: any) {
    try {
      const response = await this._axiosInstance({
        url: message.url,
        method: message.method || 'GET',
        headers: message.headers || {},
        data: message.data,
        responseType: 'stream'
      });

      const stream = response.data as Readable;

      stream.on('data', (chunk: Buffer) => {
        if (this._panel) {
          this._panel.webview.postMessage({
            command: 'sseData',
            requestId: message.requestId,
            data: chunk.toString('utf8')
          });
        }
      });

      stream.on('end', () => {
        if (this._panel) {
          this._panel.webview.postMessage({
            command: 'sseComplete',
            requestId: message.requestId
          });
        }
      });

      stream.on('error', (error: Error) => {
        if (this._panel) {
          this._panel.webview.postMessage({
            command: 'sseError',
            requestId: message.requestId,
            error: error.message
          });
        }
      });

    } catch (error: any) {
      if (this._panel) {
        this._panel.webview.postMessage({
          command: 'sseError',
          requestId: message.requestId,
          error: error.message
        });
      }
    }
  }

  private _postThemeMessage() {
    if (this._panel) {
      this._panel.webview.postMessage({
        type: 'theme-changed',
        theme: window.activeColorTheme.kind,
        isDark: window.activeColorTheme.kind === ColorThemeKind.Dark
      });
    }
  }

  private _handleUploadImgError(error: any) {
    if (error instanceof TosClientError) {
      console.log('Client Err Msg:', error.message);
      console.log('Client Err Stack:', error.stack);
    } else if (error instanceof TosServerError) {
      console.log('Request ID:', error.requestId);
      console.log('Response Status Code:', error.statusCode);
      console.log('Response Header:', error.headers);
      console.log('Response Err Code:', error.code);
      console.log('Response Err Msg:', error.message);
    } else {
      console.log('unexpected exception, message: ', error);
    }
  }

  /**
   * 生成短的随机文件名
   * @param originalName 原始文件名（用于获取扩展名）
   * @returns 短的随机文件名
   */
  private _generateShortFilename(originalName: string): string {
    const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    const length = 8;
    let result = '';
    for (let i = 0; i < length; i++) {
        result += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    const ext = originalName.split('.').pop() || '';
    return `${result}.${ext}`;
  }

  private async _uploadImg(imgName: string, imgBase64: string) {
    try {
      const bucketName = 'blueos';
      const objectName = this._generateShortFilename(imgName);
      const base64Data = imgBase64.replace(/^data:image\/\w+;base64,/, '');
      const buffer: Buffer = Buffer.from(base64Data, 'base64');

      // 上传本地文件流
      await this._tosClient.putObject({
        bucket: bucketName,
        key: objectName,
        body: buffer
      });
      if (this._panel) {
        // window.showInformationMessage('图片上传成功:' + objectName);
        this._panel.webview.postMessage({
          type: 'imgUploaded',
          imgUrl: 'https://blueos.tos-cn-guangzhou.volces.com/' + objectName
        });
      }
    } catch (error) {
      this._handleUploadImgError(error);
    }
  }

  private _setWebviewMessageListener(webview: Webview) {
    webview.onDidReceiveMessage(
      async (message: any) => {
        const command = message.command;

        switch (command) {
          case 'get-theme':
            this._postThemeMessage();
            break;
          case 'httpRequest':
            await this._handleHttpRequest(message);
            break;
          case 'streamRequest':
            await this._handleStreamChat(message);
            break;
          case 'exportPages':
            await this._handleExportPages(message.pages);
            break;
          case 'showError':
            window.showErrorMessage(message.text);
            break;
          case 'uploadImg':
            await this._uploadImg(message.imgName, message.imgBase64);
            break;
          case 'chatCancel':
            const abortController = this._controllers.get(message.requestId);
            if (abortController) {
              abortController.abort();
              this._controllers.delete(message.requestId);
              break;
            }
            const createChatData = this._createChatDatas.get(message.requestId);
            await this._cozeApi.chat.cancel(createChatData.conversation_id, createChatData.id);
            this._createChatDatas.delete(message.requestId);
            break;
        }
      },
      undefined,
      this._disposables
    );
  }

  private _getWebviewContent(webview: Webview) {
    const stylesUri = getUri(webview, this._extensionUri, ["webview-ui", "build", "assets", "index.css"]);
    const scriptUri = getUri(webview, this._extensionUri, ["webview-ui", "build", "assets", "index.js"]);
    // const nonce = getNonce();
    const nonce = '2lMU328qsiz5m7PJbUcbO7Flx4ZFVQqQ';

    // 修改 CSP，添加 blob: 支持
    return /*html*/ `
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <meta http-equiv="Content-Security-Policy" content="
            default-src 'none';
            connect-src https:; 
            style-src ${webview.cspSource} 'unsafe-inline' https://unpkg.com;
            img-src ${webview.cspSource} https: data: blob:;
            font-src ${webview.cspSource} https://unpkg.com https://at.alicdn.com;
            script-src 'nonce-${nonce}' https://unpkg.com https://at.alicdn.com;">
          <link rel="stylesheet" crossorigin nonce="${nonce}" type="text/css" href="${stylesUri}">
          <script type="module" nonce="${nonce}" src="${scriptUri}"></script>
          <title>LuHuo</title>
        </head>
        <body>
          <div id="app"></div>
        </body>
      </html>
    `;
  }

  // 复制模板文件夹
  private async _copyTemplate(src: string, dest: string) {
    const entries = await fs.promises.readdir(src, { withFileTypes: true });

    await fs.promises.mkdir(dest, { recursive: true });

    for (const entry of entries) {
      const srcPath = path.join(src, entry.name);
      const destPath = path.join(dest, entry.name);

      if (entry.isDirectory()) {
        await this._copyTemplate(srcPath, destPath);
      } else {
        await fs.promises.copyFile(srcPath, destPath);
      }
    }
  }

  /**
   * 获取唯一的目录路径
   * @param basePath - 基础路径
   * @returns 不重复的目标路径
   */
  private _getUniqueDirectoryPath(basePath: string): string {
    // 初始路径
    let targetPath: string = path.join(basePath, 'codeTemplate');
    let counter: number = 1;

    // 如果目录已存在，则添加数字后缀
    while (fs.existsSync(targetPath)) {
        targetPath = path.join(basePath, `codeTemplate${counter}`);
        counter++;
    }

    return targetPath;
  }

  private async _handleExportPages(pages: any[]) {
    try {
      // 让用户选择保存目录
      const folderUri = await window.showOpenDialog({
        canSelectFiles: false,
        canSelectFolders: true,
        canSelectMany: false,
        openLabel: '选择保存目录'
      });

      if (!folderUri || folderUri.length === 0) {
        return; // 用户取消选择
      }

      const targetPath = this._getUniqueDirectoryPath(folderUri[0].fsPath);

      // 复制模板文件
      const templatePath = path.join(this._context.extensionPath, 'codeTemplate');

      // 确保目标目录存在
      await fs.promises.mkdir(targetPath, { recursive: true });

      // 复制模板文件夹
      await this._copyTemplate(templatePath, targetPath);

      const manifestPages: Record<string, { component: string }> = {};

      // 写入生成的代码
      for (const page of pages) {
        const uxPath = path.join(targetPath, 'src', 'pages', page.name, 'index.ux');
        const dir = path.dirname(uxPath);
        await fs.promises.mkdir(dir, { recursive: true });
        await fs.promises.writeFile(uxPath, page.content, 'utf-8');
        const routePath = `pages/${page.name}`;
        manifestPages[routePath] = {component: 'index'}  
      }
      
      // 更新manifest.json文件
      const manifestPath = path.join(targetPath, 'src', 'manifest.json');
      const manifest: Manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
      manifest.router.pages = manifestPages;
      // 写回文件并保持格式
      fs.writeFileSync(
        manifestPath,
        JSON.stringify(manifest, null, 2), // 使用2空格缩进
        'utf-8'
      );

      // 提示保存成功并询问是否打开
      const action = await window.showInformationMessage(
        `代码已保存到: ${targetPath}`,
        '在 BlueOS Studio 中打开',
        '取消'
      );

      if (action === '在 BlueOS Studio 中打开') {
        try {
            const { exec, spawn } = require('child_process');
            // Windows 路径需要处理反斜杠
            const normalizedPath = targetPath.replace(/\\/g, '\\\\');
    
            if (process.platform === 'win32') {
                // Windows 系统
                const process = spawn('blue-os-studio', [`"${normalizedPath}"`], {
                  windowsHide: false,
                  shell: true
                });
      
                process.on('error', (error: any) => {
                    window.showErrorMessage(`打开 BlueOS Studio 失败: ${error.message}`);
                    // 如果执行失败，尝试使用默认程序打开
                    require('electron').shell.openPath(normalizedPath).catch((err: any) => {
                      window.showErrorMessage(`使用默认程序打开失败: ${err.message}`);
                    });
                });
            } else {
                // macOS 系统
                exec(`open -a "BlueOS Studio" "${normalizedPath}"`, (error: any) => {
                    if (error) {
                        window.showErrorMessage(`打开 BlueOS Studio 失败: ${error.message}`);
                        // 如果执行失败，尝试使用默认程序打开
                        require('electron').shell.openPath(normalizedPath).catch((err: any) => {
                            window.showErrorMessage(`使用默认程序打开失败: ${err.message}`);
                        });
                    }
                });
            }
        } catch (error: any) {
            window.showErrorMessage(`打开 BlueOS Studio 失败: ${error.message}`);
            // 如果出现异常，尝试使用默认程序打开
            try {
                require('electron').shell.openPath(targetPath);
            } catch (err: any) {
                window.showErrorMessage(`使用默认程序打开失败: ${err.message}`);
            }
        }
      }
    } catch (error: any) {
      window.showErrorMessage(`保存代码失败: ${error.message}`);
    }
  }
}
